package io;

import java.io.FileInputStream;
import java.io.FileReader;
import java.io.IOException;

/**
 * @projectName: java-sample
 * @description:
 * @author: Adam
 * @create: 2019-11-11 11:16
 **/

public class FileInputStreamTest {
    /*
      有序数据抽象成流模型
      1.Java.io 包几乎包含了所有操作输入、输出需要的类。所有这些流类代表了输入源和输出目标。
        Java.io 包中的流支持很多种格式，比如：基本类型、对象、本地化字符集等等。
      2.一个流可以理解为一个数据的序列。输入流表示从一个源读取数据，输出流表示向一个目标写数据。
       Java 为 I/O 提供了强大的而灵活的支持，使其更广泛地应用到文件传输和网络编程中。
      3.java的输入流主要由InputStream和Reader作为基类，输出流主要由OutputStream和Writer作为基类。他们都是一些抽象类，无法直接创建实例，
        但他们分别有一个用于读取文件的输入流:FileInputStream和FileReader。
      4.字节流和字符流：
        字节流和字符流的用法几乎完全一样，区别在于两者操作的数据单元不同，字节流操作的是是8位的字节，字符流操作的是16位的字符。
      5.节点流和处理流
        按照流的角色来分，可以分为节点流和处理流。
        【节点流：程序直接连接到实际的数据源，和实际的输入/输出节点连接。】
        处理流；对一个已存在的流进行连接或封装，通过封装后的流来实现数据读/写功能，处理流也被称为高级流。
        java使用处理流来包装节点流是一种典型的装饰器设计模式，处理流亦被称为包装流。
          InputStream：字节输入流,Reader：字符输出流
          OutputStream:字节输出流,Writer：字符输出流
          流中有隐式的记录指针来表示从哪里开始读取数据
          性能的提高：主要以增加缓冲的方式提高输入/输出效率
        java程序处理流可以包装来自不同源的节点流，包括硬盘，网络等。

     */

    /**
     * 字节流和字符流的区别
     * 区别：
     * 1、字节流在操作的时候本身是不会用到缓冲区（内存）的，是与文件本身直接操作的，而字符流在操作的时候是使用到缓冲区的
     * 2、字节流在操作文件时，即使不关闭资源（close方法），文件也能输出，但是如果字符流不使用close方法的话，则不会输出任何内容，
     *    说明字符流用的是缓冲区，并且可以使用flush方法强制进行刷新缓冲区，这时才能在不close的情况下输出内容
     * 3、Reader类的read()方法返回类型为int ：作为整数读取的字符（占两个字节共16位），范围在 0 到 65535 之间 (0x00-0xffff)，
     *    如果已到达流的末尾，则返回 -1,inputStream的read()虽然也返回int，但由于此类是面向字节流的，一个字节占8个位，
     *    所以返回 0 到 255 范围内的 int 字节值。如果因为已经到达流末尾而没有可用的字节，则返回值 -1。
     *    因此对于不能用0-255来表示的值就得用字符流来读取！比如说汉字.
     *
     *    使用字节流读取文本文件时，会将一个汉字劈成两半，一次只能读一半出来，产生了乱码，无法使用。就算是一次读取2个字节出来，也依然不能解决问题。
     *
     *    那开发中究竟用字节流好还是用字符流好呢？
     * 一、字符（Reader和 Writer）：中文，字符是只有在内存中才会形成的，操作字符、字符数组或字符串
     * 二、字节（InputStream 和OutputStream）：音频文件、图片、歌曲，所有的硬盘上保存文件或进行传输的时候，操作字节和字节数组或二进制对象,
     *
     * 缓冲区可以简单地理解为一段内存区域。
     * 可以简单地把缓冲区理解为一段特殊的内存。
     * 某些情况下，如果一个程序频繁地操作一个资源（如文件或数据库），则性能会很低，此时为了提升性能，就可以将一部分数据暂时读入到内存的一块区域之中，以后直接从此区域中读取数据即可，因为读取内存速度会比较快，这样可以提升程序的性能。
     * 在字符流的操作中，所有的字符都是在内存中形成的，在输出前会将所有的内容暂时保存在内存之中，所以使用了缓冲区暂存数据。
     * 如果想在不关闭时也可以将字符流的内容全部输出，则可以使用Writer类中的flush()方法完成。
     *
     * 字节流的功能比字符流的功能强大，因为计算机里所有的数据都是二进制的，而字节流可以处理所有的二进制文件。
     * 如果输入输出的是文本内容，应使用字符流，如果输入输出的是二进制内容，则应该使用字节流
     *
     * 计算机文件大致分为文本文件和二进制文件，凡是可以用记事本打开的为文本文件，反之则是二进制文件。实质上计算机中所有的文件都是二进制文件。当二进制文件里
     * 的内容恰好能被正常解析成字符时，则该二进制文件变成了文本文件。
     * 特殊的IO流：
     * 不在java.io包下的流，还有一些诸如AudioInputStream, ZipInputStream等具有访问音频文件、加密/解密、压缩/解压等功能的字节流。
     *
     */

    public static void main(String[] args) throws IOException {
         FileInputStreamTest fis = new FileInputStreamTest();
         //fis.byteStream();
         fis.characterStream();
    }


    /**
      * 字节流
      * @author jie
      * @param
      * @return
      * @date 2019/12/12 18:52
      */
    public void byteStream() throws IOException{
        //读取文件字节流为字节数组，再将字节数组转换为字符串
        // 创建字节流
        FileInputStream fis = new FileInputStream("D:\\Project\\java-sample\\src\\io\\FileInputStreamTest.java");
        // byte是java的基本数据类型，存储整型数据，占据1个字节(8 bits)，能够存储的数据范围是-128～+127
        //Byte是java.lang中的一个类，目的是为基本数据类型byte进行封装。
        //创建长度为1024的“竹筒”
        byte[] bbuf = new byte[256];
        //fis.read 从输入流中最多读取bbuf.length个字节的数据，并将其存储在字节数组b中，返回实际读取的字节数
        System.out.println("fis.read.length="+fis.read(bbuf));
        //引用传递
       /* for(byte b:bbuf){
            System.out.println(b+"--");
        }*/
        //用于保存实际读取的字节数
        int hasRead = 0;
        //使用循环来重复 “取水” 过程，每当程序从InputStream或Reader中取出一个或多个“水滴”后，记录指针自动向后移动，
        //也可以使用read()方法，不带入参来逐个读取“水滴”
        // 除此之外，InputStream和Reader中都提供了一些方法来控制记录指针的移动
        //read方法返回-1则表面到了输入流的结束点
        //如果read方法读取时只读到了半个中文字符，将会导致乱码
        while ((hasRead=fis.read(bbuf))>0){
            //取出竹筒中的水滴（字节），将字节数组转换成字符串输入
            System.out.println(new java.lang.String(bbuf,0,hasRead));
        }
        //关闭文件输入流，放在finally里面更安全
        //程序里打开的文件IO资源不属于内存里的资源，所以应该显示关闭文件IO资源，java7改写了所有IO资源类，他们都实现了AutoCloseable接口，
        // 因此都可以通过自动关闭资源的try语句来关闭这些IO流
        fis.close();
    }


    /**
      * 字符流
      * @author jie
      * @param
      * @return
      * @date 2019/12/12 18:53
      */
    public void characterStream() throws IOException {
        //自动关闭资源的try语句 来关闭文件输入流,这样可以保证输入流一定会关闭
         try (
             //创建字符流
              FileReader fr = new FileReader("D:\\Project\\java-sample\\src\\io\\FileInputStreamTest.java");
              ){
             //创建一个长度为32的“竹筒” 数组长度越大，文件的格式还原度越高
             char[] cbuf = new char[2048];
             //一下只读了长度为2048，要想继续读需要while循环（隐式指针）
             //fr.read(cbuf);
             /*for (char c:cbuf){
                 System.out.println("--"+c);
             }*/
             //用于保存实际读取的字符数
             int hasRead = 0;
             //使用循环来重复"取水"过程
             String str = "";
             while ((hasRead = fr.read(cbuf))>0){
                 //System.out.println(new java.lang.String(cbuf,0,hasRead));
                 str += new java.lang.String(cbuf,0,hasRead);
             }
             System.out.println(str);

         }catch (IOException e){
              e.printStackTrace();
         }
    }

}
